﻿Imports System.IO
Imports SharpCompress.Reader
Imports SharpCompress.Common
Imports SAUpdaterExec.InternetShortcut
Imports SAUpdaterExec.AppShortcut
Imports SACommon
Public Class UpdateFiles
    Private Shared mUpdateFiles As List(Of UpdateFile)
    Private Shared mAppDirectory As String
    Public Property VersionURL As String
#Region "Events"
    Public Event MessageChanged(Message As String)
    Public Event ProgressChanged(Message As String)
#End Region
#Region "Get Properties"
    Public ReadOnly Property FileList As List(Of UpdateFile)
        Get
            Return mUpdateFiles
        End Get
    End Property
    Public ReadOnly Property DownloadFileList As List(Of UpdateFile)
        Get
            Return (From x In mUpdateFiles Where x.DoDownload = True).ToList
        End Get
    End Property
    Public Shared ReadOnly Property InstallFileList As List(Of UpdateFile)
        Get
            Return (From x In mUpdateFiles Where x.ReferenceFile = False).ToList
        End Get
    End Property
    Public ReadOnly Property DownloadCount As Integer
        Get
            Return (From x In mUpdateFiles Where x.DoDownload = True).Count
        End Get
    End Property
    Public Property AppDirectory As String
        Get
            Return UpdateCore.FixFolderText(mAppDirectory)
        End Get
        Set(value As String)
            mAppDirectory = value
        End Set
    End Property
    Public ReadOnly Property UpdateDirectory As String
        Get
            Return UpdateCore.FixFolderText(AppDirectory) & "Update\"
        End Get
    End Property
    Public ReadOnly Property DownloadDirectory As String
        Get
            Return UpdateCore.FixFolderText(AppDirectory) & "Update\Download\"
        End Get
    End Property
    Public ReadOnly Property ExtractDirectory As String
        Get
            Return UpdateCore.FixFolderText(mAppDirectory) & "Update\Extract\"
        End Get
    End Property
#End Region
    Sub New()
        VersionURL = ""
        mUpdateFiles = New List(Of UpdateFile)
    End Sub
    Async Function GetFileControlList() As Task(Of Boolean)
        Dim tempStr As String
        Dim u As UpdateFile

        If VersionURL = "" Then Return False

        Try
            mUpdateFiles = New List(Of UpdateFile)

            Dim fd As New FileDownload
            Dim tResult = Await fd.DownloadFileToString(VersionURL)
            If tResult.Result <> SAEventArgs.Results.Success Then Return False
            tempStr = tResult.Message
            If tempStr = "" Then Throw New Exception

            tempStr = Compression.InflateString(tempStr).Trim

            If tempStr <> "" Then
                Dim uf As New StripeFile
                uf.ReadDataString(tempStr)
                For i = 0 To uf.LineItemCount - 1
                    u = New UpdateFile
                    u.Name = uf.GetLineItems(i, 0)
                    u.Directory = uf.GetLineItems(i, 1)
                    u.MD5 = uf.GetLineItems(i, 2)
                    u.CompressedMD5 = uf.GetLineItems(i, 3)
                    u.Version = uf.GetLineItems(i, 4)
                    u.UniqueFilename = uf.GetLineItems(i, 5)
                    u.RuleOption = uf.GetLineItems(i, 6)
                    u.RuleValue = uf.GetLineItems(i, 7)
                    u.DoDownload = DetermineDownloadStatus(u.RuleOption, u.RuleValue)
                    u.ForceDownload = DetermineForceDownloadStatus(u.Version)
                    u.ShortCut = uf.GetLineItems(i, 8)
                    'TODO Fix when reference files are added
                    u.ReferenceFile = False
                    mUpdateFiles.Add(u)
                Next
            End If

            'TODO Remove when reference file are added
            mUpdateFiles.Add(New UpdateFile() With {.Directory = "\", .Name = "startup.ini", .DoDownload = False, .ForceDownload = False, .ReferenceFile = True})
            mUpdateFiles.Add(New UpdateFile() With {.Directory = "\", .Name = "activation.ini", .DoDownload = False, .ForceDownload = False, .ReferenceFile = True})
            mUpdateFiles.Add(New UpdateFile() With {.Directory = "\", .Name = "pharmatrack.sdb", .DoDownload = False, .ForceDownload = False, .ReferenceFile = True})
            mUpdateFiles.Add(New UpdateFile() With {.Directory = "\", .Name = "settings.sdb", .DoDownload = False, .ForceDownload = False, .ReferenceFile = True})
            mUpdateFiles.Add(New UpdateFile() With {.Directory = "\", .Name = "information.sdb", .DoDownload = False, .ForceDownload = False, .ReferenceFile = True})
            mUpdateFiles.Add(New UpdateFile() With {.Directory = "\", .Name = "pharmatouch.exe", .DoDownload = False, .ForceDownload = False, .ReferenceFile = True})

            Return True
        Catch
            Return False
        End Try
    End Function
    Function CheckDownloaded() As SAEventArgs
        Dim tSuccess As Boolean = True
        Dim tDestinationFile As String
        Dim tMD5 As String
        Dim tCounter As Integer

        Dim a = (From x In mUpdateFiles Where x.DoDownload = True).ToList
        For Each item In a
            tCounter = 0
            tMD5 = "AA"
            tDestinationFile = String.Format("{0}{1}.ZIP", gUpdateFiles.DownloadDirectory, item.UniqueFilename)
            If File.Exists(tDestinationFile) = True Then tMD5 = Security.GenerateFileMD5(tDestinationFile)

            If tMD5.Trim <> item.CompressedMD5.Trim Or item.ForceDownload = True Then
                If File.Exists(tDestinationFile) = True Then
                    Do
                        Dim tResult = FileActions.DeleteFile(tDestinationFile)
                        If tResult.Result <> SAEventArgs.Results.Success Then
                            tCounter += 1
                            If tCounter = 3 Then Return tResult
                        End If
                    Loop
                End If

                item.DoDownload = True
                tSuccess = False
            Else
                item.DoDownload = False
            End If
        Next

        If tSuccess = False Then
            Return New SAEventArgs("", SAEventArgs.Results.GeneralFailure)
        Else
            Return New SAEventArgs("", SAEventArgs.Results.Success)
        End If
    End Function
    Function DetermineDownloadStatus(UpdateOption As UpdateOption.UpdateOptionID, RuleValue As String) As Boolean
        Dim tDownload As Boolean = False
        If UpdateOption = SACommon.UpdateOption.UpdateOptionID.AlwaysUpdate Then tDownload = True
        If UpdateOption = SACommon.UpdateOption.UpdateOptionID.UpdateIfOlderThan Then
            Return UpdateCore.CheckNewerVersion(UpdateCore.CurrentAppVersion, RuleValue)
        End If
        Return tDownload
    End Function
    Function DetermineForceDownloadStatus(Version As String) As Boolean
        If Version = UpdateCore.CurrentAppVersion Then Return True
        Return False
    End Function
    Public Function Install(RetryCount As Integer) As SAEventArgs
        Dim tResult = ExtractFiles(RetryCount)
        If tResult.Result <> SAEventArgs.Results.Success Then Return tResult

        Dim tName As String
        Dim tDestFilename As String
        Dim tOrigFilename As String
        RaiseEvent ProgressChanged("Copying extracted files to installation folder ....")

        For Each ar In InstallFileList
            tOrigFilename = ExtractDirectory & ar.Name
            tDestFilename = AppDirectory & ar.Name

            If File.Exists(tDestFilename) = True Then
                tResult = FileActions.DeleteFile(tDestFilename)
                If tResult.Result <> SAEventArgs.Results.Success Then Return tResult
            End If

            RaiseEvent MessageChanged("Copying file: " & ar.Name)
            File.Copy(tOrigFilename, tDestFilename)
            If MyApp.IsUpdate = False Then
                If ar.ShortCut = True Then
                    'DisplayMessage("Creating shortcuts.")
                    tName = ar.Name.Substring(0, InStr(ar.Name, ".") - 1)
                    CreateDesktopShortCut(tName, tDestFilename, gUpdateFiles.AppDirectory, tName)
                End If
            End If
        Next

        CleanupOldFiles()
        Return New SAEventArgs("", SAEventArgs.Results.Success)
    End Function
    Public Function ExtractFiles(RetryCount As Integer) As SAEventArgs
        Dim t As Integer
        Dim tMD5 As String
        Dim success As Boolean = True
        Dim tExtract As Boolean
        Dim tArchiveFile As String
        Dim tDestinationFile As String

        If Directory.Exists(ExtractDirectory) = False Then Directory.CreateDirectory(ExtractDirectory)

        RaiseEvent ProgressChanged("Extracting files ...")
        For Each item In InstallFileList
            tArchiveFile = String.Format("{0}{1}.ZIP", DownloadDirectory, item.UniqueFilename)
            tDestinationFile = ExtractDirectory & item.Name
            tMD5 = ""
            tExtract = False

            If File.Exists(tDestinationFile) = True Then tMD5 = Security.GenerateFileMD5(tDestinationFile)
            If tMD5.Trim <> item.MD5.Trim Or item.ForceDownload = True Then
                If File.Exists(tDestinationFile) = True Then
                    Dim tResult = FileActions.DeleteFile(tDestinationFile)
                    If tResult.Result <> SAEventArgs.Results.Success Then Return tResult
                End If
                tExtract = True
            End If

            If tExtract = True Then
                t = 0
                Do While t < RetryCount
                    success = False
                    RaiseEvent MessageChanged(String.Format("Extracting archive {0}.ZIP (Attempt:{1})", item.UniqueFilename, t))
                    t += 1

                    If ExtractZipFile(tArchiveFile, gUpdateFiles.ExtractDirectory) = False Then
                        System.Threading.Thread.Sleep(1000)
                    Else
                        tMD5 = Security.GenerateFileMD5(tDestinationFile)
                        If tMD5 = item.MD5 Then
                            t = 6
                            success = True
                        End If
                    End If
                Loop
                If success = False Then Return New SAEventArgs("", SAEventArgs.Results.GeneralFailure)
            End If
        Next
        Return New SAEventArgs("", SAEventArgs.Results.Success)
    End Function
    Sub CleanupOldFiles()
        Dim fileDeleteList As New List(Of String)

        RaiseEvent ProgressChanged("Cleaning up old files ...")
        RaiseEvent MessageChanged("")

        For Each item In Directory.GetFiles(gUpdateFiles.AppDirectory)
            If InUpdateList(item) = False Then fileDeleteList.Add(item)
        Next
        For Each item In fileDeleteList
            FileActions.DeleteFile(item)
        Next
    End Sub
    Function InUpdateList(Filename As String) As Boolean
        Dim fi As FileInfo = New FileInfo(Filename)
        For Each item In gUpdateFiles.FileList
            If item.Name.Trim.ToUpper = fi.Name.Trim.ToUpper Then Return True
        Next
        Return False
    End Function
    Private Function ExtractZipFile(archiveFilenameIn As String, outFolder As String) As Boolean
        Try
            Using stream As Stream = File.OpenRead(archiveFilenameIn)
                Dim reader = ReaderFactory.Open(stream)
                While reader.MoveToNextEntry()
                    If Not reader.Entry.IsDirectory Then
                        RaiseEvent MessageChanged("Processing " & reader.Entry.Key)
                        Application.DoEvents()
                        reader.WriteEntryToDirectory(outFolder, ExtractOptions.ExtractFullPath Or ExtractOptions.Overwrite)
                    End If
                End While
            End Using
            Return True
        Catch ex As Exception
            RaiseEvent MessageChanged("Archive could not be installed. Error: " & ex.GetBaseException.Message)
            Return False
        End Try
    End Function
    Private Sub CreateDesktopShortCut(ByVal Title As String, ExecutablePath As String, StartUpPath As String, Description As String)
        Dim DesktopFolder As String = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
        Dim tLink As String = String.Format("{0}\{1}.lnk", DesktopFolder, Title)
        If System.IO.File.Exists(tLink) = False Then
            'Try
            '    Dim WshShell As WshShellClass = New WshShellClass
            '    Dim MyShortcut As IWshShortcut
            '    ' The shortcut will be created on the desktop
            '    MyShortcut = CType(WshShell.CreateShortcut(String.Format("{0}\{1}.lnk", DesktopFolder, Title)), IWshShortcut)
            '    MyShortcut.Description = Description
            '    MyShortcut.TargetPath = ExecutablePath  'Specify target app full path
            '    MyShortcut.WorkingDirectory = StartUpPath
            '    MyShortcut.Save()
            'Catch ex As Exception
            '    MessageBox.Show(String.Format("Could not create the shortcut{0}{1}", Environment.NewLine, ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            'End Try

            ' Get Enums names from PathTo to set Target(PathTo...) from ComboBox.
            Dim GetEnum As Type = GetType(PathTo)
            Dim Selected As String = DirectCast("Desktop", String)
            Dim GetValue As PathTo = DirectCast([Enum].Parse(GetEnum, Selected), PathTo)
            '' Get Enums names from ProcessWindowStyle to set window style from ComboBox.
            'Dim GetStyle As Type = GetType(ProcessWindowStyle)
            'Dim SelectedStyle As String = DirectCast(ComboBoxRun.SelectedItem, String)
            'Dim GetValueStyle As ProcessWindowStyle = DirectCast([Enum].Parse(GetStyle, SelectedStyle), ProcessWindowStyle)
            ' Here is function that creates shortcut.
            AddLnkShortcut(Title, ExecutablePath, StartUpPath, "", "", ExecutablePath, 0, ProcessWindowStyle.Maximized, Target(CType(GetValue, PathTo)), Title)
        End If
    End Sub
#Region "Classes"
    Public Class UpdateFile
        Property Name As String
        Property Directory As String
        Property MD5 As String
        Property CompressedMD5 As String
        Property UniqueFilename As String
        Property Version As String
        Property RuleOption As Integer
        Property RuleValue As String
        Property DoDownload As Boolean
        Property ForceDownload As Boolean
        Property ReferenceFile As Boolean
        Property ShortCut As Boolean
        Public ReadOnly Property InstallDirectory As String
            Get
                Dim tString As String = UpdateCore.FixFolderText(Directory.Replace("<%APP%>\", ""))
                Return UpdateCore.FixFolderText(mAppDirectory) & tString
            End Get
        End Property
    End Class
#End Region
End Class
